package xiaomandown_common

import (
	"bufio"
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/sha1"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

func AesEncrypt(orig string, key string) string {
	// 转成字节数组
	origData := []byte(orig)
	k := []byte(key)

	// 分组秘钥
	block, err := aes.NewCipher(k)
	if err != nil {
		panic(fmt.Sprintf("key 长度必须 16/24/32长度: %s", err.Error()))
	}
	// 获取秘钥块的长度
	blockSize := block.BlockSize()
	// 补全码
	origData = PKCS7Padding(origData, blockSize)
	// 加密模式
	blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
	// 创建数组
	cryted := make([]byte, len(origData))
	// 加密
	blockMode.CryptBlocks(cryted, origData)
	// 使用RawURLEncoding 不要使用StdEncoding
	// 不要使用StdEncoding  放在url参数中回导致错误
	return base64.RawURLEncoding.EncodeToString(cryted)

}

func AesDecrypt(cryted string, key string) string {
	// 使用RawURLEncoding 不要使用StdEncoding
	// 不要使用StdEncoding  放在url参数中回导致错误
	crytedByte, _ := base64.RawURLEncoding.DecodeString(cryted)
	k := []byte(key)

	// 分组秘钥
	block, err := aes.NewCipher(k)
	if err != nil {
		panic(fmt.Sprintf("key 长度必须 16/24/32长度: %s", err.Error()))
	}
	// 获取秘钥块的长度
	blockSize := block.BlockSize()
	// 加密模式
	blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
	// 创建数组
	orig := make([]byte, len(crytedByte))
	// 解密
	blockMode.CryptBlocks(orig, crytedByte)
	// 去补全码
	orig = PKCS7UnPadding(orig)
	return string(orig)
}

// 补码
func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
	padding := blocksize - len(ciphertext)%blocksize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

// 去码
func PKCS7UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func Sha1String(data string) string {
	sha1 := sha1.New()
	sha1.Write([]byte(data))
	return hex.EncodeToString(sha1.Sum([]byte(nil)))
}

func RandStr1(n int) string {
	var bytes []byte = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890")
	result := make([]byte, n)
	for i := 0; i < n; i++ {
		result[i] = bytes[rand.Int31()%62]
	}
	return string(result)
}

func HttpGet(urls string, attrs map[string]string, version string, macId string) (HttpResp, error) {
	var respBody HttpResp

	uid := macId
	nowTime := time.Now().Unix()
	nowStr := strconv.FormatInt(nowTime, 10)
	nonce := RandStr1(12)
	token := ""

	token = Sha1String(uid + nowStr + nowStr + nonce + uid)

	urls = urls + "?mac=" + uid + "&token=" + token + "&t=" + nowStr + "&nonce=" + nonce + "&ver=" + version

	if len(attrs) > 0 {
		for k, v := range attrs {
			urls = urls + "&" + url.QueryEscape(k) + "=" + url.QueryEscape(v)
		}
	}
	//fmt.Printf("url:%+v\n",url)
	resp, err := http.Get(urls)
	//fmt.Printf("err:%+v\n", err)
	if err != nil {
		return respBody, fmt.Errorf("HTTP GET:%v", err)
	}

	if resp.StatusCode != http.StatusOK {
		return respBody, errors.New("http code error")
	}

	html, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(html, &respBody)
	if err != nil {
		return respBody, err
	}

	return respBody, nil
}

func MD5(v string) string {
	d := []byte(v)
	m := md5.New()
	m.Write(d)
	return hex.EncodeToString(m.Sum(nil))
}

func GetMacAdders() string {
	var str string
	netInterfaces, err := net.Interfaces()
	if err != nil {
		fmt.Printf("fail to get net interfaces: %v", err)
		str = "127.0.0.1"
	}

	macAddrs := []string{}
	for _, netInterface := range netInterfaces {
		macAddr := netInterface.HardwareAddr.String()
		if len(macAddr) == 0 {
			continue
		}

		macAddrs = append(macAddrs, macAddr)
	}

	str = macAddrs[0]
	str = MD5(str)
	return str
}

func PathExist(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, err
	}
	return false, err
}

func FileSet(path string, content string) error {
	file, err := os.OpenFile(path, os.O_CREATE|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		return fmt.Errorf("OpenFile err:%v\n", err)
	}
	defer file.Close()
	str := content
	str = AesEncrypt(str, AESKey)
	writer := bufio.NewWriter(file)
	writer.WriteString(str)
	// writer带缓存，需要刷到磁盘
	writer.Flush()

	return nil
}

func FileGet(path string) (string, error) {
	b, err := PathExist(path)
	if err != nil {
		return "", err
	}

	// 如果不存在则生成这个空文件
	if !b {
		err := FileSet(path, "")
		if err != nil {
			return "", errors.New("无权限操作数据文件")
		}

		return "", nil
	}

	filePath := path
	// 一次性读取文件内容，适合文件不大的情况。没有显式打开文件，不用关闭文件。open close已封装到ReadFile函数中
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		return "", fmt.Errorf("read file error:%v\n", err)
	}
	str := AesDecrypt(string(content), AESKey)
	return str, nil
}

func HttpGetForLog(l string, s error) (string, error) {
	address := ServerAddressReportLog + "?s=" + url.QueryEscape(s.Error()) + "&url=" + url.QueryEscape(l)
	resp, err := http.Get(address)
	if err != nil {
		return "", fmt.Errorf("HTTP GET:%v", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", errors.New("http code error")
	}

	html, _ := ioutil.ReadAll(resp.Body)
	return string(html), nil
}

func GetProjectPath() string {
	var projectPath string
	projectPath, _ = os.Getwd()
	return projectPath
}
